﻿using GTMC.Communi;
using GTMC.Enum;
using Leeder.Logger;
using Leeder.Util;
using SPSAndong.Comm;
using SPSAndong.Handle;
using SPSAndong.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;

namespace SPSAndong.Business
{
    public class SPSAndongBLL
    {
        /// <summary>
        /// 日志
        /// </summary>
        LogOperate LogObj => Log.GetLogger(this.GetType());
        /// <summary>
        /// 按钮呼叫超时线程
        /// </summary>
        CancellationTokenSource cancel_buttonStateTimeOut;
        /// <summary>
        /// 按钮呼叫线程
        /// </summary>
        CancellationTokenSource cancel_buttonState;
        /// <summary>
        /// 按钮状态改变事件
        /// </summary>
        public event Action OnButtonStateChanged;
        /// <summary>
        /// 设备状态改变事件
        /// </summary>
        public event Action OnDeviceStateChanged;
        /// <summary>
        /// 加班时间改变事件
        /// </summary>
        public event Action OnOuttimeChanged;
        /// <summary>
        /// 主要设备监控线程
        /// </summary>
        public CancellationTokenSource cancel_mainequip;
        /// <summary>
        /// 定义ups服务变量
        /// </summary>
        private UpsHandle upsServer;
        /// <summary>
        /// 程序编号
        /// </summary>
        public ProgramCode ProgramCode { get; set; } = ProgramCode.Program01;
        ToyopucHandler toyopucHandler;
        IndirectAddress address;
        TriaModel triaModel;

        ProjectSetBLL projectSetBLL = new ProjectSetBLL();

        /// <summary>
        /// 单例模式
        /// </summary>
        private static SPSAndongBLL instance = new SPSAndongBLL();
        public static SPSAndongBLL getInstance()
        {
            if (instance == null)
                instance = new SPSAndongBLL();
            return instance;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        private SPSAndongBLL()
        {

        }
        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            //InitOpcDaServer();
            InitToyopuc();
            InitTask();
        }

        public void InitToyopuc()
        {
            toyopucHandler = new ToyopucHandler("192.168.1.5", 3000);
            address = new IndirectAddress();
            address.Identifier = "D";
            address.Address = 0x000;
            List<short> data = new List<short>();
            data.Add(255);
            //toyopucHandler.WriteExtString(ProgramCode, address, data);
            //byte[] result = toyopucHandler.ReadExtString(ProgramCode, address, 10);
        }

        /// <summary>
        /// 初始化OpcServer
        /// </summary>
        public void InitOpcDaServer()
        {
            OpcConnectBLL bllOpcConnect = new OpcConnectBLL();
            OpcDeviceBLL bllOpcDevice = new OpcDeviceBLL();
            OpcGroupBLL bllOpcGroup = new OpcGroupBLL();
            OpcTagBLL bllOpcTag = new OpcTagBLL();

            //获取OPC的 连接 组 TAG
            OpcConnectModel opcConnect = bllOpcConnect.GetModel();
            List<OpcDeviceModel> opcDeviceList = bllOpcDevice.GetModelAll();
            List<OpcGroupModel> opcGroupList = bllOpcGroup.GetModelAll();
            List<OpcTagModel> opcTagList = bllOpcTag.GetModelAll();
            GolbalVariable.OpcTagBag = new ConcurrentBag<OpcTagModel>(opcTagList);
            GolbalVariable.OpcGroupList = opcGroupList;

            try
            {
                //获取服务名称，并创建URL
                Uri uri = UrlBuilder.Build(opcConnect.OpcServerProgId);
                GolbalVariable.OpcHelper.opcServer = new OpcDaServer(uri);
                //绑定事件，并且启动OPC重连线程
                GolbalVariable.OpcHelper.opcServer.ConnectionStateChanged += GolbalVariable.OpcHelper.Server_ConnectionStateChanged;
                GolbalVariable.OpcHelper.opcServer.Shutdown += GolbalVariable.OpcHelper.Server_Shutdown;
                GolbalVariable.OpcHelper.opcServer.Connect();

                //依次遍历opcDevices->opcGroups->opcTags，创建OPC分组，并绑定ValuesChanged事件
                var opcDevices = opcDeviceList.Where(p => p.ConnectId == opcConnect.ID);
                foreach (var opcDevice in opcDevices)
                {
                    var opcGroups = opcGroupList.Where(p => p.DeviceId == opcDevice.ID);
                    foreach (var opcGroup in opcGroups)
                    {
                        OpcDaGroup opcDaGroup = GolbalVariable.OpcHelper.opcServer.AddGroup(opcGroup.GroupName);
                        List<OpcDaItemDefinition> list = new List<OpcDaItemDefinition>();
                        var opcTags = opcTagList.Where(p => p.GroupId == opcGroup.ID);
                        if (opcTags.Count() == 0) continue;
                        foreach (var opcTag in opcTags)
                        {
                            if (string.IsNullOrEmpty(opcTag.TagName))
                                continue;
                            string ItemId = opcDevice.DeviceName + "." + opcGroup.GroupName + "." + opcTag.TagName;
                            list.Add(new OpcDaItemDefinition { ItemId = ItemId, IsActive = true });
                            opcTag.TagName = ItemId;
                        }
                        var results = opcDaGroup.AddItems(list);
                        opcDaGroup.UpdateRate = TimeSpan.FromMilliseconds(opcGroup.UpdateRate);
                        //根据组名分别订阅不同的逻辑事件
                        switch (opcGroup.GroupName)
                        {
                            case "buttonAdderss":
                                opcDaGroup.ValuesChanged += ButtonAdderssGroup_ValuesChanged;  //绑定ValuesChanged事件
                                break;
                            case "ioCommunicationAddress":
                                opcDaGroup.ValuesChanged += IoCommunicationAddressGroup_ValuesChanged;  //绑定ValuesChanged事件
                                break;
                            case "outtimeAddress":
                                opcDaGroup.ValuesChanged += OuttimeAddressGroup_ValuesChanged;  //绑定ValuesChanged事件
                                break;
                        }

                        opcDaGroup.IsActive = true;
                        opcDaGroup.IsSubscribed = true;
                        //GolbalVariable.OpcDaGroupList.Add(opcDaGroup);
                        //记录错误信息.
                        foreach (OpcDaItemResult result in results)
                        {
                            if (result.Error.Failed)
                                LogObj.Error(result.Item == null ? "" : result.Item.ItemId + " 变量错误，请核对变量名称");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogObj.Error(ex.Message);
            }
        }

        /// <summary>
        /// 初始化线程
        /// </summary>
        private void InitTask()
        {
            cancel_buttonStateTimeOut = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                ButtonStateTimeOut();
            }, cancel_buttonStateTimeOut.Token);

            cancel_buttonState = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                ButtonState();
            }, cancel_buttonState.Token);

            //主要设备监控线程
            if (GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP").Any())
            {
                cancel_mainequip = new CancellationTokenSource();
                LogObj.Info("开启主要设备监控线程启动");
                Task.Factory.StartNew(() =>
                {
                    BindMainPower();
                }, cancel_mainequip.Token);
            }

            //创建ups监控对象的实例
            upsServer = new UpsHandle();
            //接收数据事件
            upsServer.Received += UpsServer_Received;
            //初始化com口
            LogObj.Info("ups监控线程开启");
            upsServer.InitCom();
        }

        /// <summary>
        /// 呼叫按钮状态改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonAdderssGroup_ValuesChanged(object sender, OpcDaItemValuesChangedEventArgs e)
        {
            try
            {
                foreach (OpcDaItemValue value in e.Values)
                {
                    if (value == null || value.Value == null)
                        continue;
                    var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
                    if (info != null)
                    {
                        int result = 0;
                        //判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
                        if (value.Value is bool)
                            result = (Convert.ToBoolean(value.Value) ? 1 : 0);
                        else
                            int.TryParse(value.Value.ToString(), out result);
                        //result==1时为过点信息,result==0时不作处理
                        if (result == 0)
                            continue;
                        OnButtonStateChanged?.BeginInvoke(null, null);
                        //业务处理
                        MonitorButtonStateChange(info.SignalMark, result);
                    }
                }
            }
            catch (Exception ex)
            {
                LogObj.Error(ex.Message);
            }
        }

        /// <summary>
        /// 系统设备故障监控
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IoCommunicationAddressGroup_ValuesChanged(object sender, OpcDaItemValuesChangedEventArgs e)
        {
            try
            {
                foreach (OpcDaItemValue value in e.Values)
                {
                    if (value == null || value.Value == null)
                        continue;
                    var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
                    if (info != null)
                    {
                        int result = 0;
                        //判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
                        if (value.Value is bool)
                            result = (Convert.ToBoolean(value.Value) ? 1 : 0);
                        else
                            int.TryParse(value.Value.ToString(), out result);
                        OnDeviceStateChanged?.BeginInvoke(null, null);
                        //业务处理
                        DeviceStateChange(info.SignalMark, result);
                    }
                }
            }
            catch (Exception ex)
            {
                LogObj.Error(ex.Message);
            }
        }

        /// <summary>
        /// 加班时间改变事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OuttimeAddressGroup_ValuesChanged(object sender, TitaniumAS.Opc.Client.Da.OpcDaItemValuesChangedEventArgs e)
        {
            try
            {
                foreach (OpcDaItemValue value in e.Values)
                {
                    if (value == null || value.Value == null)
                        continue;
                    var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
                    if (info != null)
                    {
                        int result = 0;
                        //判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
                        if (value.Value is bool)
                            result = (Convert.ToBoolean(value.Value) ? 1 : 0);
                        else
                            int.TryParse(value.Value.ToString(), out result);
                        GolbalVariable.Overtime = result;
                        OnOuttimeChanged?.BeginInvoke(null, null);
                        //业务处理
                    }
                }
            }
            catch (Exception ex)
            {
                LogObj.Error(ex.Message);
            }
        }

        /// <summary>
        /// 按钮信号处理
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">状态</param>
        private void MonitorButtonStateChange(string signalMark, int state)
        {
            //查找OPC触发对应的按钮
            var plans = GolbalVariable.ProjectBag.Where(x => x.SignalMark == signalMark).ToList();
            if (plans != null)
            {
                plans.ForEach(x =>
                {
                    if (x.State != state)
                    {
                        if (x.State == 0)
                        {
                            //正常状态下按下按钮该部品状态变为呼叫状态
                            x.ActualStartTime = DateTime.Now; //按钮状态为,1，开始呼叫，记录实际开始时间                                                            
                            GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, true);//将讲呼岗位加入音乐播放队列
                                                                                         //写入LED显示数据库表：呼叫异常显示：0：正常(完成)、1：异常呼叫、2：呼叫超时
                            x.State = 1;

                            //保存呼叫记录
                            LogObj.Info(x.AreaName + "->" + x.WorkName + ":异常呼叫");
                        }
                        else
                        {
                            //呼叫或超时呼叫状态下按下按钮该部品状态变为正常状态
                            x.ActualEndTime = DateTime.Now; //按钮状态为,0，结束呼叫，记录实际结束时间                                                             
                            GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, true);//将讲呼岗位加入音乐播放队列
                                                                                         //写入LED显示数据库表：呼叫异常显示：0：正常(完成)、1：异常呼叫、2：呼叫超时
                            x.State = 0;
                            if (GolbalVariable.ProjectBag.Count(s => s.State != 0 && s.AreaType == x.AreaType) == 0)
                                GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, false, "", Convert.ToInt32((DateTime.Now - x.ActualStartTime.Value).TotalSeconds)); //将结束音乐播放，加入队列

                            //保存呼叫记录
                            LogObj.Info(x.AreaName + "->" + x.WorkName + ":呼叫完成");
                        }
                    }
                });
                projectSetBLL.UpdateList(plans);
            }
        }
        /// <summary>
        /// 设备状态改变
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">状态</param>
        private void DeviceStateChange(string signalMark, int state)
        {
            //查找OPC触发对应的设备
            var plans = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark).ToList();
            if (plans != null)
                plans.ForEach(x =>
                {
                    x.Status = state;
                });
        }

        string lastResultStr = "";
        /// <summary>
        /// 按钮呼叫线程
        /// </summary>
        private async void ButtonState()
        {
            while (!this.cancel_buttonState.Token.IsCancellationRequested)
            {
                //查询当前按钮的状态
                byte[] result = toyopucHandler.ReadExtString(ProgramCode, address, 10);

                string resultStr = ByteToStr(result);

                if (lastResultStr != resultStr)
                {
                    lastResultStr = resultStr;
                    int packetIndex = 0;
                    if (triaModel == null)
                        triaModel = new TriaModel();
                    if (triaModel.TriaGroupList == null)
                        triaModel.TriaGroupList = new List<TriaGroup>();
                    for (int j = 0; j < 16; j++)
                    {
                        try
                        {
                            TriaGroup triaGroup;
                            if ((j + 1) > triaModel.TriaGroupList.Count)
                            {
                                triaGroup = new TriaGroup();
                                triaModel.TriaGroupList.Add(triaGroup);
                            }
                            else
                            {
                                triaGroup = triaModel.TriaGroupList[j];
                            }
                            var groupStates = resultStr.FindIndexString(ref packetIndex, 16);
                            if (triaGroup.StateStrs != groupStates)
                            {
                                triaGroup.GroupName = "D" + j;
                                if (triaGroup.States == null)
                                    triaGroup.States = new List<bool>();
                                for (int i = 0; i < groupStates.Length; i++)
                                {
                                    if ((i + 1) > triaGroup.States.Count)
                                    {
                                        if (groupStates[i] == 0)
                                            triaGroup.States.Add(false);
                                        else
                                            triaGroup.States.Add(true);

                                        var item = int.Parse(groupStates[i].ToString());
                                        MonitorButtonStateChange("D" + j + "_" + i, item);
                                        await Task.Delay(20);
                                    }
                                    else
                                    {
                                        if (triaGroup.StateStrs[i] != groupStates[i])
                                        {
                                            var item = int.Parse(groupStates[i].ToString());
                                            MonitorButtonStateChange("D" + j + "-" + i, item);
                                            await Task.Delay(20);

                                            if (groupStates[i] == 0)
                                            {
                                                triaGroup.States[i] = false;
                                            }
                                            else
                                            {
                                                triaGroup.States[i] = true;
                                            }
                                        }
                                    }

                                }
                                triaGroup.StateStrs = groupStates;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogObj.Error(ex.Message);
                            break;
                        }
                    }
                }
                //休眠
                await Task.Delay(200);
            }
        }

        /// <summary>
        /// 异常呼叫状态超时线程
        /// </summary>
        private async void ButtonStateTimeOut()
        {
            while (!this.cancel_buttonStateTimeOut.Token.IsCancellationRequested)
            {
                //查询当前正在呼叫的按钮
                var datas = GolbalVariable.ProjectBag.Where(x => x.State == 1).ToList();
                if (datas.Count > 0)
                {
                    foreach (var item in datas)
                    {
                        if (item.ActualStartTime != null)
                        {
                            if ((DateTime.Now - item.ActualStartTime.Value).TotalSeconds >= GolbalVariable.AppSet.ExceptionCall)
                            {
                                //当异常呼叫的时间超过配置时间，默认30秒
                                item.UpdateTime = DateTime.Now;
                                item.IsCallOutTime = true;
                                item.State = 2;
                                projectSetBLL.Update(item);
                            }
                        }
                    }
                }
                //休眠
                await Task.Delay(GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan);
            }
        }

        private bool plcStatus = true;
        private bool lipStatus = true;
        /// <summary>
        /// 绑定硬件设备
        /// </summary>
        private async void BindMainPower()
        {
            string plcIp = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "PLC").FirstOrDefault().DeviceIpAddress;
            string lipIp = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "LIP").FirstOrDefault().DeviceIpAddress;
            while (true)
            {
                if (cancel_mainequip.Token.IsCancellationRequested)
                {
                    cancel_mainequip.Dispose();
                    break;
                }
                plcStatus = PublicHelper.PingIp(plcIp);
                lipStatus = PublicHelper.PingIp(lipIp);
                if (!plcStatus && GolbalVariable.PlcState != false)
                {
                    LogObj.Error("PLC连接异常");
                    GolbalVariable.PlcState = false;
                }
                else
                { GolbalVariable.PlcState = plcStatus; }
                if (!lipStatus && GolbalVariable.LipState != false)
                {
                    LogObj.Error("LIP连接异常");
                    GolbalVariable.LipState = false;
                }
                else
                { GolbalVariable.LipState = lipStatus; }
                OnDeviceStateChanged?.BeginInvoke(null, null);
                await Task.Delay(5000);
            }
        }
        /// <summary>
        /// UPS接收数据事件
        /// </summary>
        /// <param name="data"></param>
        private void UpsServer_Received(UpsModel data)
        {
            //把UPS当前状态更新到缓存中（用于界面状态的更新）
            GolbalVariable.UpsStatus = data;
            //数据处理
            UpsBusiness.CheckUps(data);
            OnDeviceStateChanged?.BeginInvoke(null, null);
        }

        /// <summary>
        /// 字节数组转二进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private string ByteToStr(byte[] bytes)
        {
            string strResult = "";
            for (int i = 0; i < bytes.Length; i++)
            {
                string strTemp = System.Convert.ToString(bytes[i], 2);
                strTemp = strTemp.Insert(0, new string('0', 8 - strTemp.Length));
                //倒叙
                strTemp = ReverseD(strTemp);
                strResult += strTemp;
            }
            return strResult;
        }

        /// <summary>
        /// 二进制字符串转字节数组
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private byte[] StrToByte(string str)
        {
            byte[] bytes = new byte[str.Length / 8];
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = Convert.ToByte(str.Substring(i * 8, 8), 2);
            }
            return bytes;
        }

        public string ReverseD(string text)
        {
            return new string(text.ToCharArray().Reverse().ToArray());
        }
    }
}
